package top.lywivan.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @desc
 * @author:ivan.liu
 */
public class NioClient {

    public static void main(String[] args) throws IOException {
        //创建Channel通过
        SocketChannel socketChannel = SocketChannel.open();
        //设置为非阻塞模式
        socketChannel.configureBlocking(false);
        //创建Selector
        Selector selector = Selector.open();
        //创建Reactor线程
        new Thread(new SingleReactorClient(selector,socketChannel)).start();
    }
}

class SingleReactorClient implements Runnable{

    private final SocketChannel socketChannel;

    private final Selector selector;

    public SingleReactorClient(Selector selector,SocketChannel socketChannel){
        this.socketChannel=socketChannel;
        this.selector=selector;
    }

    @Override
    public void run() {
        try {
            //连接服务器，并发送消息
            doConnect(selector,socketChannel);
        }catch (IOException e){
            e.printStackTrace();
            System.exit(1);
        }
        //轮询Channel，处理事件
        while (true){
            try {
                //阻塞1秒
                selector.select(1000);
                //轮询事件
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                //遍历处理
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    processKey(selectionKey);
                    iterator.remove();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void processKey(SelectionKey selectionKey) throws IOException {
        if (selectionKey.isValid()){
            //根据不同的事件，分别处理
            if (selectionKey.isConnectable()){
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                if (socketChannel.finishConnect()){
                    //注册
                    socketChannel.register(this.selector,SelectionKey.OP_READ);
                    doService(socketChannel);
                }else {
                    //连接失败
                    System.exit(1);
                }
            }
            if (selectionKey.isReadable()){
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                int len = socketChannel.read(byteBuffer);
                if (len>0){
                    byteBuffer.flip();
                    byte[] bs=new byte[len];
                    byteBuffer.get(bs);
                    System.out.println(new String(bs));
                }else if (len<0){
                    selectionKey.cancel();
                    socketChannel.close();
                }
            }
        }
    }

    private void doConnect(Selector selector, SocketChannel socketChannel) throws IOException {
        System.out.println("客户端开始连接服务端");
        boolean connect = socketChannel.connect(new InetSocketAddress("127.0.0.1", 8888));
        //连接成功，则注册channel
        if (connect){
            socketChannel.register(selector,SelectionKey.OP_READ);
            System.out.println("成功连接服务器，准备发送消息");
            doService(socketChannel);
        }else {
            //连接失败，则注册连接事件
            socketChannel.register(selector,SelectionKey.OP_CONNECT);
        }
    }

    /**
     * 向channel发送数据
     * @param socketChannel
     */
    private void doService(SocketChannel socketChannel) throws IOException {
        byte[] bs="client request....".getBytes();
        ByteBuffer byteBuffer = ByteBuffer.allocate(bs.length);
        byteBuffer.put(bs);
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
    }
}
